#ifndef Sprite_h
#define Sprite_h

#include <cstdlib>
#include <math.h>
#include "SDL/SDL.h"

/*
(sprite name:string([length:s]{(:char)}))
[image count:s]
{
	string
}
[rectangle count:s]
{
	rect((x:s)(y:s)(w:s)(h:s))
}
[num animations:s]
{
	(delay:s)
	[num frames]
	{
		[clip count:s]
		{
			clip((imageIndex:s)(rectIndex:s)(offset:point((x:s)(y:s)))
		}
	}
}
*/

#include "graphics.h"
#include "spoint.h"
#include "srect.h"
#include "sclip.h"
#include "sframe.h"
#include "sanimation.h"
#include "spritesystem.h"
#include "templatearray.h"

class Sprite
{
private:
	char * m_name;
	/** bitwise combinations of SDL_FLIP_* */
	int m_flipState;
	/** animations used by this sprite */
	TemplateArray<SAnimation> m_animations;
	/** whether to animate during the update */
	bool m_running;
	/** whether to animate backwards */
	bool m_runningInReverse;
	/** if true, iterate 0 to N, then reset to 0. if false, iterate 0 to N, then N to 0, and repeat */
	bool m_loopsToBeginning;
	/** how many milliseconds the current frame has been shown */
	Uint32 m_timer;
	/** which frame is being drawn */
	int m_frameIndex;
	/** which animation is being drawn */
	int m_animationIndex;
	/** images that this sprite uses. (not managed: will not deallocate images on release) */
	TemplateArray<BitMaP*> m_surfaces;
	/** image clips used by the sprite. not ALL the clips, just ones that were allocated during load */
	TemplateArray<ImageClip> m_imageClips;
public:
	static const int FLIP_HORIZONTAL = 1;
	static const int FLIP_VERTICAL = 2;
	static const int FLIP_HORIZONTAL_AND_VERTICAL = 2;

	int get_SDL_FLIP_state();
	void set_SDL_FLIP_state(const int & a_flipstate);

	BitMaP * getSourceImage(const int & a_index){return m_surfaces.get(a_index);}
	int getSourceImageCount(){return m_surfaces.size();}

	/**
	 * @param an image index that this sprite has (in m_imageClips)
	 * @return the minimum size requirements this sprite has on this image
	 */
	SPoint requiredDimensionsForImg(const int & a_index)
	{
		BitMaP * bmp = m_surfaces.get(a_index);
		SRect area;
		// iterators
		SAnimation * sa;	SFrame * sf;	SClip * sc;	ImageClip * ic;
		for(int a = 0; a < m_animations.size(); ++a)
		{
			sa = &(m_animations.get(a));
			for(int f = 0; f < sa->getFrames()->size(); ++f)
			{
				sf = &(sa->getFrames()->get(f));
				for(int c = 0; c < sf->getClips()->size(); ++c)
				{
					sc = &(sf->getClips()->get(c));
					ic = sc->getClip();
					if(ic->image == bmp)
					{
						area.add(ic->clip);
					}
				}
			}
		}
		return area.getDimension();
	}

	/** @return index of the given image (-1 if unknown) */
	int indexOfImage(BitMaP * const & a_bmp)
	{
		return m_surfaces.indexOf(a_bmp);
	}

	/**
	 * @param a_index which index to change
	 * @param a_bmp which image to use at the index
	 */
	void setImage(const int & a_index, BitMaP * a_bmp)
	{
		BitMaP * bmp = m_surfaces.get(a_index);
		// iterators
		SAnimation * sa;	SFrame * sf;	SClip * sc;	ImageClip * ic;
		for(int a = 0; a < m_animations.size(); ++a)
		{
			sa = &(m_animations.get(a));
			for(int f = 0; f < sa->getFrames()->size(); ++f)
			{
				sf = &(sa->getFrames()->get(f));
				for(int c = 0; c < sf->getClips()->size(); ++c)
				{
					sc = &(sf->getClips()->get(c));
					ic = sc->getClip();
					if(ic->image == bmp)
					{
						ic->image = a_bmp;
					}
				}
			}
		}
		m_surfaces.set(a_index, a_bmp);
	}

	/** @return 0 if the replacement was fine, or an error code */
	char* replaceSourceImage(BitMaP * const & a_original, BitMaP * a_replacement)
	{
		int originalIndex = indexOfImage(a_original);
		// image to replace is not in here...
		if(originalIndex < 0)	return "don't have that image";
		SPoint replacementArea = a_replacement->getArea().getDimension();
		SPoint requiredArea = requiredDimensionsForImg(originalIndex);
		// replacing image is not good enough...
		if(replacementArea.getX() < requiredArea.getX()
		|| replacementArea.getY() < requiredArea.getY())
			return "new image not big enough";
		setImage(originalIndex, a_replacement);
		return 0;
	}

	Sprite();
	void release();
	~Sprite();
	Sprite(const char * a_fileName,const int & a_maxFrames, const int & a_animationSpeed);
	Sprite(const SDL_Surface * a_surfaces, const int & a_maxFrames,const int & a_animationSpeed);
	void set(const char * a_spriteName, const TemplateArray<BitMaP*> * a_images, const short * a_data);
	void set(const TemplateArray<BitMaP*> * a_images, const short * a_data);
	Sprite(const char * a_spriteName, const TemplateArray<BitMaP*> * a_images, const short * a_data);
	Sprite(const char * a_filename, const short * a_data);

	void draw(SDL_Surface* a_screen, const SPoint & a_point);
	void draw(SDL_Surface* a_screen, const SPoint & a_point, const int & a_zoom);
	void start();
	void restart();
	bool update(const int & a_msPassed);
	void setLoopToBegin(bool loop);
	bool running() const;
	void stop();
	inline int getFrameIndex() const {return m_frameIndex;}
	inline void setFrameIndex(const int & a_index){m_frameIndex=a_index;}
	inline int getAnimationIndex() const {return m_animationIndex;}
	inline void setAnimationIndex(const int & a_index){m_animationIndex=a_index;}
	inline int getAnimationCount() const {return m_animations.size();}
	inline SAnimation * getAnimation(const int & a_index) const {return &m_animations.get(a_index);}
	inline SAnimation * getAnimation() const {return getAnimation(getAnimationIndex());}
	inline int getFrameCount() const {return getAnimation()->getFrameCount();}
	inline void setFrameIndexSafe(int a_index)
	{
		while(a_index < 0)a_index += getFrameCount();
		if(a_index >= getFrameCount())a_index %= getFrameCount();
		setFrameIndex(a_index);
	}
	inline void setAnimationIndexSafe(int a_index)
	{
		while(a_index < 0)a_index += getAnimationCount();
		if(a_index >= getAnimationCount())a_index %= getAnimationCount();
		setAnimationIndex(a_index);
		setFrameIndex(m_frameIndex);
	}
	inline SFrame * getFrame(const int & a_index) const
	{
		if(a_index >= 0 && a_index < getFrameCount() && getAnimation())
			return getAnimation()->getFrame(a_index);
		return 0;
	}
	inline SFrame * getFrame() const
	{
		return getFrame(m_frameIndex);
	}
	SAnimation * addAnimation()
	{
		return m_animations.add();
	}
	SFrame * addFrame()
	{
		SAnimation * sa = 0;
		if(getAnimationCount() == 0)
		{
			sa = addAnimation();
		}
		else
		{
			sa = &m_animations.getLast();
		}
		return sa->getFrames()->add();
	}
	inline TemplateArray<SAnimation> * getAnimations(){return &m_animations;}

	bool isSprite() const;

	inline bool isInReverse(){return m_runningInReverse;}

	void setName(const char * a_spriteName);

	const char * getName(){return m_name;}

	// TODO
//	bool rectCollide() const;
//	bool pixelCollide() const;
//	void rotate90();
//	void rotate180();
//	void rotate270();
//	void flipHorizontal();
//	void flipVertical();
//	void fade(const float & fade); // fade from 0 to 100%
//	void reverseAnimation();

//	void zoom(const float & z);  // percentage to zoom in
//	void stretchX(const float & x); // percentage to stretchheight
//	void stretchY(const float & y);  // percentage to stretchwidth
//	void stretch(const float & x, const float & y); // percentage to strech X and Y
};
#endif
